/****************************************************************************
 * Copyright 2009 Huascar A. Sanchez                                        *
 *                                                                          *
 * Licensed under the Apache License, Version 2.0 (the "License");          *
 * you may not use this file except in compliance with the License.         *
 * You may obtain a copy of the License at                                  *
 *                                                                          *
 *     http://www.apache.org/licenses/LICENSE-2.0                           *
 *                                                                          *
 * Unless required by applicable law or agreed to in writing, software      *
 * distributed under the License is distributed on an "AS IS" BASIS,        *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 * See the License for the specific language governing permissions and      *
 * limitations under the License.                                           *
 ****************************************************************************/
package com.gotobject.filter;

import com.gotobject.filter.builders.RestrictedFilteringBuilder;
import static com.gotobject.filter.internal.Objects.as;
import static com.gotobject.filter.internal.Pair.of;
import com.gotobject.filter.internal.ToStrings;
import static com.gotobject.filter.internal.ToStrings.str;

import com.gotobject.filter.spi.ContextFluency;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author huascar.sanchez@gmail.com (Huascar A. Sanchez)
 */
public class FilterImpl implements Filter {
    private final List<Throwable> errors;
    private final ContextFluency apiCheck;
    private final AtomicReference<RestrictedFilteringBuilder<?>> builder;

    /**
     * default constructor of the Filter implementation.
     */
    public FilterImpl(){
        this(new ArrayList<Throwable>(), new ContextFluency());
    }

    /**
     * construct a new {@code FilterImpl}.
     * @param errors
     *      container of errors.
     * @param apiCheck
     *      ensure that conditions are called in the right place.
     */
    public FilterImpl(List<Throwable> errors, ContextFluency apiCheck){
        this.errors     = errors;
        this.apiCheck   = apiCheck;
        builder         = new AtomicReference<RestrictedFilteringBuilder<?>>();
    }
    
    @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
    public void addError(String message){
        addError(makeException(message));
    }    

    @Override
    public void addError(Throwable cause) {
       if(!errors.contains(cause)) errors.add(cause);
    }

    @Override
    public <T> RestrictedFilteringBuilder<T> from(Iterable<T> source) {
        builder.compareAndSet(builder.get(), new RestrictedFilteringBuilderImpl<T>(this, source));
        return as(builder.get());
    }

    public ContextFluency getApiFluencyCheck() {
        return apiCheck;
    }
    
    private static Exception makeException(String t) {
        try {
            final Constructor<Exception> c = Exception.class.getDeclaredConstructor(String.class);
            return c.newInstance(String.format("Internal error when checking prerequisites. %s", t));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void throwFailedFilteringExceptions(){
        if(errors.isEmpty()){
            return;
        }
        throw new FilteringException(errors);
    }

    @Override
    public String toString() {
        return ToStrings.toString(
                this, 
                of("builder", str(builder.get())),
                of("errors", str(errors))
        );
    }
}
